﻿
using DataService.Core.Common;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Xml.Linq;

namespace DataService.Core.Extensions
{
    public static class FieldHelper
    {
        #region 获取表达式属性

        public static List<string> GetExpressMemberInfos<T>(Expression<Func<T, bool>> expression)
        {
            List<string> propertyInfos = new List<string>();
            Resolve<T>(expression, ref propertyInfos);
            propertyInfos = propertyInfos.Distinct().ToList();
            return propertyInfos;
        }

        public static void Resolve<T>(Expression expression, ref List<string> propertyInfos, Expression bodyLeft = null)
        {
            if (expression is LambdaExpression)
            {
                LambdaExpression lambda = expression as LambdaExpression;
                expression = lambda.Body;
                Resolve<T>(expression, ref propertyInfos);
            }
            if (expression is BinaryExpression)
            {
                BinaryExpression binary = expression as BinaryExpression;
                if (binary.Left is MemberExpression & binary.Right is ConstantExpression)//解析x=>x.Name=="123" x.Age==123这类
                    ResolveFunc(binary.Left, binary.Right, binary.NodeType, ref propertyInfos);
                if (binary.Left is MethodCallExpression & binary.Right is ConstantExpression)//解析x=>x.Name.Contains("xxx")==false这类的
                {
                    object value = (binary.Right as ConstantExpression).Value;
                    ResolveLinqToObject<T>(binary.Left, value, ref propertyInfos, binary.NodeType);
                }
                if (binary.Left is MemberExpression & binary.Right is MemberExpression)//解析x=>x.Date==DateTime.Now这种
                {
                    //优化时，可以通过前缀判断 等号右边是否是值；等号右边两种情况   如 x.width>x.height ；decimal 变量名=5, x.value=变量名
                    string Name = (binary.Left as MemberExpression).Member.Name;
                    propertyInfos.Add(Name);
                }
            }
            if (expression is UnaryExpression)
            {
                UnaryExpression unary = expression as UnaryExpression;
                if (unary.Operand is MethodCallExpression)//解析!x=>x.Name.Contains("xxx")或!array.Contains(x.Name)这类
                    ResolveLinqToObject<T>(unary.Operand, false, ref propertyInfos);
                if (unary.Operand is MemberExpression & unary.NodeType == ExpressionType.Not)//解析x=>!x.isDeletion这样的 
                {
                    ConstantExpression constant = Expression.Constant(false);
                    ResolveFunc(unary.Operand, constant, ExpressionType.Equal, ref propertyInfos);
                }
                if (unary.Operand.Type.ToString().Contains("System.Guid"))
                {
                    ResolveFunc(bodyLeft, expression, ExpressionType.Equal, ref propertyInfos);
                }
            }
            if (expression is MemberExpression & expression.NodeType == ExpressionType.MemberAccess)//解析x=>x.isDeletion这样的 
            {
                MemberExpression member = expression as MemberExpression;
                if (member.Type.ToString().Contains("System.Boolean"))
                {
                    ConstantExpression constant = Expression.Constant(true);
                    ResolveFunc(member, constant, ExpressionType.Equal, ref propertyInfos);
                }
                //PropertyInfo propertyInfo = member.Member as PropertyInfo;
                //string sss = propertyInfo.PropertyType.ToString();
            }
            if (expression is MethodCallExpression)//x=>x.Name.Contains("xxx")或array.Contains(x.Name)这类
            {
                MethodCallExpression methodcall = expression as MethodCallExpression;
                ResolveLinqToObject<T>(methodcall, true, ref propertyInfos);
            }
            if (expression is ConstantExpression && expression.NodeType == ExpressionType.Constant)
            {
            }
            var body = expression as BinaryExpression;
            if (body == null)
            {
            }
            else
            {
                Resolve<T>(body.Left, ref propertyInfos);
                Resolve<T>(body.Right, ref propertyInfos, body.Left);

            }
        }


        public static void ResolveFunc(Expression left, Expression right, ExpressionType expressiontype, ref List<string> propertyInfos)
        {
            if (!left.Type.ToString().Contains("System.Boolean") && right.Type.ToString().Contains("System.Boolean"))
            {
                string Name = (left as MemberExpression).Member.Name;
                propertyInfos.Add(Name);
            }
            else if (left.Type.ToString().Contains("System.Boolean") && right is ConstantExpression && right.NodeType == ExpressionType.Constant)
            {
                string Name = (left as MemberExpression).Member.Name;
                propertyInfos.Add(Name);
            }
            else if (left.Type.ToString().Equals("System.Guid"))
            {
                string Name = (left as MemberExpression).Member.Name;
                propertyInfos.Add(Name);
            }
            else if (left.Type.ToString().Equals("System.Nullable`1[System.Guid]"))
            {
                string Name = (left as MemberExpression).Member.Name;
                propertyInfos.Add(Name);
            }
            else
            {
                string Name = (left as MemberExpression).Member.Name;
                propertyInfos.Add(Name);
            }

        }

        public static void ResolveLinqToObject<T>(Expression expression, object value, ref List<string> propertyInfos, ExpressionType? expressiontype = null)
        {
            try
            {
                var MethodCall = expression as MethodCallExpression;
                var MethodName = MethodCall.Method.Name;
                switch (MethodName)//这里其实还可以改成反射调用，不用写switch
                {
                    case "Contains":
                        if (MethodCall.Object == null)
                            In(MethodCall, value, ref propertyInfos);
                        if (MethodCall.Object != null && MethodCall.Object.Type.Name == "List`1")
                            throw new Exception(string.Format("不支持List.Contains，请使用string[].Contains", MethodName));
                        if (MethodCall.Object != null)
                            Like(MethodCall, ref propertyInfos);
                        break;
                    case "Count":
                        Len(MethodCall, ref propertyInfos);
                        break;
                    case "LongCount":
                        Len(MethodCall, ref propertyInfos);
                        break;
                    case "Equals":
                        Equals<T>(expression, ref propertyInfos);
                        break;
                    case "EndsWith":
                    case "StartsWith":
                        Like(MethodCall, ref propertyInfos);
                        break;
                    default:
                        throw new Exception(string.Format("不支持{0}方法的查找！", MethodName));
                }
            }
            catch (Exception ex)
            {
                throw new Exception("不支持方法的查找！");
            }

        }
        private static void In(MethodCallExpression expression, object isTrue, ref List<string> propertyInfos)
        {
            string fieldName = (expression.Arguments[1] as MemberExpression).Member.Name;
            propertyInfos.Add(fieldName);
        }


        public static void Like(MethodCallExpression expression, ref List<string> propertyInfos)
        {
            string Name = (expression.Object as MemberExpression).Member.Name;
            propertyInfos.Add(Name);
        }

        private static void Len(MethodCallExpression expression, ref List<string> propertyInfos)
        {
            object Name = (expression.Arguments[0] as MemberExpression).Member.Name;
            propertyInfos.Add(Name.ToString());

        }


        private static void Equals<T>(Expression expression, ref List<string> propertyInfos)
        {
            string paraName = ((expression as MethodCallExpression).Object as MemberExpression).Member.Name;
            propertyInfos.Add(paraName);
        }
        #endregion




        public static List<string> GetFieldArray<T>(Expression<Func<T, object>> func)
        {
            var properties = func.Body.Type.GetProperties();
            PropertyInfo[] propertyInfos = typeof(T).GetProperties().Where(p => properties.Any(x => p.Name == x.Name)).ToArray();
            return propertyInfos.Select(x => x.Name).ToList();
        }


        public static long GetMaxValue<T>(List<T> sourceList, string valueField)
        {
            long maxValue = 0;
            PropertyInfo? propertyInfo = typeof(T).GetProperty(valueField);
            if (propertyInfo == null) return 0;
            foreach (var item in sourceList)
            {
                object value = propertyInfo.GetValue(item);
                if (value == null)
                {
                    continue;
                }
                long tempVal = value.ToLong();
                if (tempVal > maxValue)
                {
                    maxValue = tempVal;
                }
            }
            return maxValue;
        }

        public static long GetMinValue<T>(List<T> sourceList, string valueField)
        {
            if (sourceList?.Count == 0)
                return 0;
            List<long> valueList = new List<long>();
            PropertyInfo? propertyInfo = typeof(T).GetProperty(valueField);
            if (propertyInfo == null) return 0;
            foreach (var item in sourceList)
            {
                object value = propertyInfo.GetValue(item);
                if (value == null)
                {
                    continue;
                }
                long tempVal = value.ToLong();
                valueList.Add(tempVal);
            }
            long minValue = valueList.Min();
            return minValue;
        }
    }
}
